fix: include generated media in run output regardless of store_media setting#6793
Open
Br1an67 wants to merge 13 commits intoagno-agi:mainfrom
Open
fix: include generated media in run output regardless of store_media setting#6793Br1an67 wants to merge 13 commits intoagno-agi:mainfrom
Br1an67 wants to merge 13 commits intoagno-agi:mainfrom
Conversation
Mustafa-Esoofally
added a commit
that referenced
this pull request
Mar 2, 2026
Apply the same store_media decoupling to Team that PR #6793 applied to Agent: remove if-guards on store_media_util() in all 6 team run paths and add save/restore of media fields around cleanup_and_store so callers still see generated media when store_media=False. Add unit tests verifying sync and async agent.run() returns images in RunOutput even with store_media=False. Closes #5101
Apply the same store_media decoupling to Team that PR agno-agi#6793 applied to Agent: remove if-guards on store_media_util() in all 6 team run paths and add save/restore of media fields around cleanup_and_store so callers still see generated media when store_media=False. Add unit tests verifying sync and async agent.run() returns images in RunOutput even with store_media=False. Closes agno-agi#5101
If persistence fails between scrub and restore, media would be left scrubbed on the returned RunOutput. Wrapping in try/finally guarantees the caller always sees media regardless of DB errors.
Tests cover both non-streaming and streaming Slack paths with store_media=False, including real Agent end-to-end scenarios. Verifies media reaches upload_response_media_async in both paths.
Cover sync/async streaming with both stream_events=True and yield_run_output=True to verify images survive cleanup_and_store.
b2d325c to
16cefe1
Compare
When cache_session=True and store_media=False, the finally-block media restore in cleanup_and_store rehydrated media on the same RunOutput object stored in session.runs (reference aliasing). On the next run, save_session would re-persist the old run with restored media, leaking it to DB despite store_media=False. Shallow-copy run_response before upsert_run so session.runs holds an independent snapshot of the scrubbed state.
scrub_media_from_run_output() scrubbed input/messages media but not top-level output fields (images/videos/audio/files). This caused member responses and workflow executor runs to leak media to DB when store_media=False, since _scrub_member_responses and _store_executor_response delegate to this function. The fix adds 4 lines to null these fields. This is safe because cleanup_and_store saves/restores media refs in try/finally, so callers still receive media — only the DB copy is scrubbed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Decouples
store_mediafrom in-run media availability. Previously,store_media=Falseprevented media from appearing in both the DB and the returnedRunOutput, making it impossible to forward generated images/videos/audio to external services (WhatsApp, Slack) immediately after a run.Now
store_mediacontrols only DB persistence. Generated media is always available on the returnedRunOutputfor the caller to act on.Closes #5101
Bugs Fixed
This PR fixes 4 distinct bugs in the media pipeline:
Bug 1:
store_media=Falsestrips media from RunOutputstore_media_util()— which copies media fromModelResponsetoRunOutput— was gated behindif agent.store_media:in all 10 run paths (4 agent + 6 team). Whenstore_media=False, media was never placed onRunOutput, so callers couldn't forward images to WhatsApp/Slack.Fix: Remove the gate.
store_media_util()always runs. DB scrubbing happens later incleanup_and_store.Bug 2: Streaming images gated on
store_media(asymmetry)In
handle_model_response_chunk, only images were gated behindif agent.store_media:. Videos, audio, and files were never gated. This meant streaming withstore_media=Falsedropped images but not other media types.Fix: Remove the gate. All media types are now consistent — always collected into
run_responseduring streaming.Bug 3: Session cache aliasing leaks media back to DB
After
cleanup_and_storescrubbed media and stored the run,session.upsert_run(run=run_response)stored a reference to the sameRunOutputobject. If media was later restored on that object (e.g., viatry/finally), subsequentsave_session()calls would re-persist the old run with restored media — leaking it to DB despitestore_media=False.Fix:
session.upsert_run(run=copy.copy(run_response))— shallow copy breaks reference aliasing so the session cache holds an independent scrubbed snapshot.Bug 4:
scrub_media_from_run_outputmissed top-level output fieldsscrub_media_from_run_output()scrubbed input and message media but didn't null the top-level output fields (images,videos,audio,files). This caused team member responses and workflow executor runs to leak media to DB whenstore_media=False, because_scrub_member_responsesand_store_executor_responsedelegate to this function.Fix: Add 4 lines to null top-level output fields. Safe because
cleanup_and_storeusesstorage_copy(caller's original is untouched).Full Data Lifecycle
Files Changed
agent/_response.pystore_mediagate on streaming image collection (Bug 2)agent/_run.pystore_mediagates in 4 run paths (Bug 1);storage_copypattern incleanup_and_store+acleanup_and_store(Bug 3)team/_run.pystorage_copyin_cleanup_and_store+_acleanup_and_store(Bug 3)utils/agent.pyscrub_media_from_run_output(Bug 4)tests/unit/agent/test_store_media_run_output.pytests/unit/agent/test_store_media_scrub_leak.pytests/unit/os/routers/test_slack_store_media.pyTest Coverage (25 tests)
Agent core (8 tests) —
test_store_media_run_output.pystore_media=Truestill worksScrub + DB isolation (10 tests) —
test_store_media_scrub_leak.pyscrub_media_from_run_outputnulls all 4 media types (images/videos/audio/files)RunOutputandTeamRunOutputscrub_run_output_for_storagestore_media=Truepreserves media (regression guard)cleanup_and_storerestores media for caller (sync + async)store_media=FalseSlack integration (7 tests) —
test_slack_store_media.pystore_media=Falsestore_media=Falsestore_media=Truestill uploadsDesign Decisions
Why
copy.copy()instead ofdeepcopy?Shallow copy is sufficient because we only need to break top-level field aliasing (
storage_copy.images = Nonedoesn't affectrun_response.images). Deep copy would unnecessarily clone large media objects. Input/message media IS shared between the copy and original — this is acceptable becausescrub_run_output_for_storagehas always scrubbed those on the original in pre-PR code.Why explicit nulling after
scrub_run_output_for_storage?Defensive —
scrub_media_from_run_outputnow also nulls these fields, but the explicit block makes the intent clear at thecleanup_and_storecall site.Interaction with
_scrub_member_responses:For team member responses,
scrub_media_from_run_outputis called permanently (no save/restore). The top-level nulling fix (Bug 4) is critical here — without it, member-generated images leak to DB even when the member hasstore_media=False.Related Issues
store_media=Falsestrips media from RunOutput — can't forward images to WhatsAppstore_media=Falseto avoid bloat can now still act on media in the current runstore_media=False+store_tool_messages=Falsenow get working media without DB growthType of change
Checklist
./scripts/format.shand./scripts/validate.sh)